import React, { useState, useEffect, useRef, KeyboardEvent } from 'react';
import classnames from 'classnames';
import Input, { InputProps } from '../Input';
import Icon from '../Icon';
import Transition from '../Transition';

import useDebounce from '../../hooks/useDebounce';
import useClickOutside from '../../hooks/useClickOutside';

// 定义列表的数据结构
interface AutoCompleteDataSourceObject {
  value: string;
}

// 通过泛型使组件能够处理更多结构的数据类型来展示列表
export type AutoCompleteDataSourceType<T = {}> = AutoCompleteDataSourceObject &
  T;

export interface AutoCompleteProps extends Omit<InputProps, 'onSelect'> {
  // 支持异步和同步过滤list，异步返回 Promise
  fetchSuggestions: (
    keyword: string
  ) =>
    | Array<AutoCompleteDataSourceType>
    | Promise<Array<AutoCompleteDataSourceType>>;

  onSelect?: (item: AutoCompleteDataSourceType) => void;
  renderOption?: (item: AutoCompleteDataSourceType) => React.ReactElement;
}

const AutoComplete: React.FC<AutoCompleteProps> = ({
  fetchSuggestions,
  onSelect,
  renderOption,
  value,
  ...props
}) => {
  /**
   * 修改 input 的输入值
   */
  const [inputValue, setInputValue] = useState<string>(value as string);

  /**
   * 使用参数传递的函数过滤到结果集合，存放所有要选择的选项
   */
  const [suggestions, setSuggestions] = useState<AutoCompleteDataSourceType[]>(
    []
  );

  /**
   * 显示 loading 效果
   */
  const [loading, setLoading] = useState<boolean>(false);

  /**
   * 用来确定list中的高亮项
   */
  const [highlightIndex, setHighlightIndex] = useState<number>(-1);

  /**
   * 判断是输入还是选择，输入才执行自定义过滤操作 fetchSuggestions
   */
  const triggeredSearch = useRef(false);

  /**
   * 当前组件的 Ref
   */
  const componentRef = useRef<HTMLDivElement>(null);

  /**
   * 输入框输入防抖， inputValue 变化会导致 debounceValue变化，
   * debounceValue 变化会重新调用下面的 effect
   */
  const debouncedValue = useDebounce(inputValue, 300);

  useClickOutside(componentRef, () => {
    setSuggestions([]);
  });

  useEffect(() => {
    // console.log('===========防抖了======================');
    // console.log(debouncedValue);
    // console.log('==========防抖了===============');
    if (debouncedValue && triggeredSearch.current) {
      const results = fetchSuggestions(debouncedValue);
      if (results instanceof Promise) {
        setLoading(true);
        results.then(data => {
          setSuggestions(data);
          setLoading(false);
        });
      } else {
        setSuggestions(results);
      }
    } else {
      setSuggestions([]);
    }

    // 每次重置 list 之后都要清除当前高亮项为初始值
    setHighlightIndex(-1);
  }, [debouncedValue, fetchSuggestions]);

  // 每次 onChange 都要过滤满足条件的集合
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    triggeredSearch.current = true;
    const value = e.target.value.trim();
    setInputValue(value);
  };

  /**
   * 候选项别选中时触发，根据传入的过滤函数，显示对应的数据 list
   * @param item 当前选中的 item
   */
  const handleSelect = (item: AutoCompleteDataSourceType) => {
    triggeredSearch.current = false;
    setInputValue(item.value);
    setSuggestions([]);
    onSelect && onSelect(item);
  };

  /**
   * 选择项高亮边界判断方法
   * @param index
   */
  const triggerHighlight = (index: number) => {
    if (index < 0) index = 0;
    if (index >= suggestions.length) index = suggestions.length - 1;
    setHighlightIndex(index);
  };

  /**
   * 响应键盘事件
   * @param e
   */
  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    switch (e.keyCode) {
      case 13:
        if (suggestions[highlightIndex]) {
          handleSelect(suggestions[highlightIndex]);
        }
        break;
      case 38:
        triggerHighlight(highlightIndex - 1);
        break;
      case 40:
        triggerHighlight(highlightIndex + 1);
        break;
      case 27:
        setSuggestions([]);
        break;
    }
  };

  /**
   * 根据是否传入了自定义渲染方式进行渲染
   * @param item 每一条 item
   */
  const renderTemplate = (item: AutoCompleteDataSourceType) => {
    return renderOption ? renderOption(item) : item.value;
  };

  /**
   * 根据过滤完成的 list，生成对应的列表供选择
   */
  const genDropDown = () => {
    return (
      <Transition
        in={suggestions.length > 0}
        timeout={300}
        animation='zoom-in-top'
      >
        <ul className='auto-complete__list'>
          {suggestions.map((item: AutoCompleteDataSourceType, index) => {
            const classes = classnames('list-item', {
              'highlighted-item': index === highlightIndex,
            });
            return (
              <li
                className={classes}
                key={index}
                onClick={() => handleSelect(item)}
              >
                {renderTemplate(item)}
              </li>
            );
          })}
        </ul>
      </Transition>
    );
  };

  return (
    <div className='auto-complete' ref={componentRef}>
      <Input
        value={inputValue}
        onChange={handleChange}
        {...props}
        onKeyDown={handleKeyDown}
      />
      {loading && (
        <div className='auto-complete__loading'>
          <Icon icon='spinner' spin />
        </div>
      )}
      {genDropDown()}
    </div>
  );
};

export default AutoComplete;
